#pragma once
#ifndef __FIXED_ARRAY_H_INCLUDED__
#define __FIXED_ARRAY_H_INCLUDED__

#include <glf/fstl/fixed_base.h>

namespace glf {

template<typename T, size_t N>
struct fixed_array : public iterable<T> {
	typedef T				value_type;
	typedef T*				pointer;
	typedef const pointer	const_pointer;
	typedef T&				reference;
	typedef const T&		const_reference;
	typedef size_t			size_type;
	typedef ptrdiff_t		difference_type;
	
	typedef typename iterable<T>::iterator			iterator;
	typedef typename iterable<T>::const_iterator	const_iterator;	

	typedef typename iterable<T>::reverse_iterator			reverse_iterator;
	typedef typename iterable<T>::const_reverse_iterator	const_reverse_iterator;

	iterator begin() { 
		return this->mElems; 
	}
	const_iterator begin() const { 
		return this->mElems; 
	}
	iterator end() { 
		return this->mElems + N; 
	}
	const_iterator end() const { 
		return this->mElems + N; 
	}
	reverse_iterator rbegin() { 
	   return reverse_iterator(end()); 
	}
	const_reverse_iterator rbegin() const {
		return const_reverse_iterator(end());
	}
	reverse_iterator rend() { 
		return reverse_iterator(begin()); 
	}
	const_reverse_iterator rend() const {
		return const_reverse_iterator(begin());
	}

	// operator[]
	reference operator[](size_type i) { 
		GLF_ASSERT( i < N && "out of range" ); 
		return mElems[i];
	}

	const_reference operator[](size_type i) const {     
		GLF_ASSERT( i < N && "out of range" ); 
		return mElems[i]; 
	}

	// at() with range check
	reference at(size_type i) { 
		rangecheck(i); 
		return mElems[i]; 
	}
	const_reference at(size_type i) const { 
		rangecheck(i); 
		return mElems[i]; 
	}

	// front() and back()
	reference front() { 
		return mElems[0]; 
	}

	const_reference front() const {
		return mElems[0];
	}

	reference back() { 
		return mElems[N-1]; 
	}

	const_reference back() const { 
		return mElems[N-1]; 
	}

	// size is constant
	static size_type size() { 
		return N; 
	}
	static bool empty() { 
		return false; 
	}
	static size_type max_size() { return N; }
	enum { static_size = N };

	// swap (note: linear complexity)
	void swap (fixed_array<T,N>& y) {
		for (size_type i = 0; i < N; ++i) {
			//boost::swap(mElems[i],y.mElems[i]);
		}
	}

	// direct access to data (read-only)
	const T* data() const { 
		return mElems; 
	}
	T* data() { 
		return mElems; 
	}
	// use fixed_array as C fixed_array (direct read/write access to data)
	T* c_array() { 
		return mElems; 
	}

	// assignment with type conversion
	template <typename T2>
	fixed_array<T,N>& operator= (const fixed_array<T2,N>& rhs) {
		std::copy(rhs.begin(),rhs.end(), begin());
		return *this;
	}

	// assign one value to all elements
	void assign (const T& value) {
		std::fill_n(begin(),size(),value);
	}

	// check range (may be private because it is static)
	static void rangecheck (size_type i) {
		if (i >= size()) {
			//throw std::out_of_range("array<>: index out of range");
		}
	}
	T mElems[N];
};

}

#endif